package at.ac.tuwien.prog.entity;

import at.ac.tuwien.prog.Util;

/**
 * 
 * @author christoph.pickl@student.tuwien.ac.at
 */
public class Register {

	private final int intValue;
	
	private byte[] byteValue;
	
	private String bitString;
	
	private Register(int intValue) {
		this.intValue = intValue;
	}
	
	public static Register newByInt(int intValue) {
		return new Register(intValue);
	}
	
	public static Register newBy4SubBytes(final byte[] bytes, final int offset) {
		return Register.newByBytes(new byte[] { 
				bytes[offset + 0],
				bytes[offset + 1],
				bytes[offset + 2],
				bytes[offset + 3]
		});
	}

	public static Register newByBytes(final byte[] bytes) {
		if(bytes.length != 4) {
			throw new IllegalArgumentException("Register: bytes.length ["+bytes.length+"] != 4");
		}
		
		int resultInt = 0;
		for (int i = bytes.length - 1; i >= 0 ; i--) {
			final byte b = bytes[i];
			
//			LOG.trace("XXX "+b+" => " + Util.toBitString(b));
			
			int mask = 1;
			for (int j = 0; j < 8; j++) {
				resultInt |= mask & b;
				mask <<= 1;
			}
//			LOG.trace("YYY "+resultInt+" => " + Register.newByInt(resultInt).toBitString());
			
			
//			LOG.trace("#"+i+": b=["+new Register(b).toBitString()+"]; resultInt=" + new Register(resultInt).toBitString());
			if(i != 0) {
				resultInt <<= 8;
			}
		}
		return new Register(resultInt);
	}
	
	
	

	/*
	
	private static int composeIntBy4Bytes(byte[] bytes) {
		int result = 0;
		
//		ystem.out.println("bytes:");
		for (int i = bytes.length-1; i >= 0 ; i--) {
//			ystem.out.print(" " + Util.toBitSequence(new byte[] {bytes[i]}));
		}
//		ystem.out.println();
		
		for (int i = bytes.length-1; i >= 0; i--) {
			result <<= 8;
			int widenedByte = saveWidenByte(bytes[i]);
//			LOG.trace((i+1)+". byte:   " + Util.toBitSequence(widenedByte) + " (widened: "+widenedByte+")");
			result = result | widenedByte;
//			LOG.trace("   result: " + Util.toBitSequence(result));
		}
		
		return result;
	}
	
	private static int saveWidenByte(final byte b) {
		int result = 0;
		result |= 255 & b;
		return result;
	}

	*/
	
	
	@Override
	public String toString() {
		return "Register[intValue="+intValue+"; bitString="+this.toBitString()+"]";
	}
	
	public int getIntValue() {
		return this.intValue;
	}

	/**
	 * @return 4 bytes for int number = 32bit
	 */
	public byte[] toBytes() {
		if(this.byteValue == null) {
			
			int byteMask = 255; // 0000 0000 0000 0000 0000 0000 1111 1111
			this.byteValue = new byte[4];
			
			for (int i = 0; i < this.byteValue.length; i++) {
	//			LOG.trace("source:      " + Util.toBitSequence(number));
	//			LOG.trace((i+1) +". byteMask: " + Util.toBitSequence(byteMask));
				
				final int invalidShiftedByte = (int) (this.intValue &  byteMask);
	//			LOG.trace("XXXXXX:      " + Util.toBitSequence(invalidShiftedByte));
				
				final byte b = (byte) (invalidShiftedByte >> i * 8);
				
	//			LOG.trace("   masked result: " + Util.toBitSequence(new byte[] {b}));
				this.byteValue[i] = b;
				byteMask <<= 8;
			}
			
		}
		
		return this.byteValue;
	}

	/**
	 * for debugging purposes only
	 */
	public String toBitString() {
		if(this.bitString == null) {
			this.bitString = Util.toBitString(this.intValue);
		}
		
		return this.bitString;
	}


	@Override
	public boolean equals(Object other) {
		if(other == this) {
			return true;
		}
		if((other instanceof Register) == false) {
			return false;
		}
		final Register that = (Register) other;
		return this.intValue == that.intValue;
	}

	@Override
	public int hashCode() {
		return Integer.valueOf(this.intValue).hashCode();
	}
}
